import {ClassMetaData, Meta} from "./public/Meta";
import {MapString, sdf} from "./public/sdf";
import {df} from "./public/df";
import {BindField, BindFunc} from "./BindField";

export enum expType{
    int,
    number,
    string,
    object,
    file,
}

export class IFieldRes<T> {
    /**
     * 接收到的值
     */
    val: T;
    /**
     * 错误提示
     */
    msg: string = exp.errMsg_;

    //字段缺省值
    defaul: T;

    /**
     * 验证成功与否
     */
    ok: boolean = false;

    /**
     * xss攻击过滤
     */
    xssFilter() {
        if (typeof this.val === "string") {
            this.val = sdf.xssFilter(this.val as any) as any;
        }
        return true;
    }
}


export interface IncomingFormParse {
    encoding: string;
    uploadDir: string;
    keepExtensions: boolean;
    //最大字段长度
    maxFieldsSize: number;
    maxFields: number;
    hash: string | boolean;
    multiples: boolean;
    type: string;
    bytesReceived: number;
    bytesExpected: number;

    //最大文件字节长度
    maxFileSize?: number;

    //文件开始下载前检测
    checkFile?: (name: string, file: FileMultiple) => boolean;

    //是否关闭文件上传严格模式(未在modReq对象里指定的exp.file()的都不允许上传)
    disableStrict?: boolean;

    /**
     * 所有已上传文件
     */
    fileList?: FileMultiple[];

    fieldsMap: MapString<string | FileMultiple | any[]>;

    modReqInst: any;
}

export interface FileMultiple {
    size: number;
    path: string;
    name: string;
    type: string;
    lastModifiedDate?: Date;
    hash?: string;

    toJSON(): Object;
}

interface BindAbele<T> {
    /**
     * 验证数据是否有效
     */
    _check(): IFieldRes<T>;
}


export class BindAsync {

    checkFunc = (b: BindAsync) => {

    }

    timeFunc: any = null;

    loadMsg = "加载中...";

    isLoading = false;

    /**
     * 输入框中的值
     */
    val: any = "";

    /**
     * 验证成功
     */
    ok() {
        this.isOk = true;
        this.isLoading = false;
        this.loadCall("");
    }

    /**
     * 验证失败
     * @param msg
     */
    error(msg: string) {
        this.isOk = false;
        this.isLoading = false;
        this.errMsg = msg;
        this.loadCall(msg);
    }

    isOk = false;
    errMsg = "";

    loadCall(msg: string) {

    }

    init(val: any) {
        this.isLoading = true;
        this.isOk = false;
        this.val = val;
        if (this.timeFunc)
            clearTimeout(this.timeFunc);
        this.timeFunc = setTimeout(() => {
            try {
                this.checkFunc(this);
            } catch (e) {
                df.onCatchError(e);
            }

            this.timeFunc = null;
        }, 300);
    }


}

export class exp {
    static errMsg_ = "参数无效";
    static netErr = "网络异常";

    static reqFormDat = "__fromData__";

    constructor(public func: (res: IFieldRes<any>) => boolean, public type: expType) {

    }


    /**
     * 设置class的验证字段
     * @param obj
     * @param key
     * @param func
     */
    static setFieldCheckMetaData<T>(obj: ClassMetaData, key: string, func: (o: IFieldRes<T>) => boolean) {
        Meta.setData(obj, "fieldCheckMap", key, func);
    }

    static getFieldCheckMetaData(obj: ClassMetaData, key: string) {
        return Meta.getData(obj, "fieldCheckMap", key) as (o: IFieldRes<any>) => boolean;
    }


    static isFile(req: any) {
        return req instanceof exp;
    }

    /**
     *
     * @param target
     */
    static noValid(target: { new(): any; }) {
        Meta.setData(target, "exp.noValid", "", true);
    }

    static getNoValid(target: { new(): any; }) {
        return Meta.getData(target, "exp.noValid", "") as boolean;
    }

    static notCheck(target: { new(): any; }) {
        Meta.setData(target, "exp.notCheck", "", true);
    }

    static getNotCheck(target: { new(): any; }) {
        return Meta.getData(target, "exp.notCheck", "") as boolean;
    }

    /**
     * multipart/form-data文件上传
     */
    static multipart(func: (mutl: IncomingFormParse) => void) {
        return (target: { new(): any; }) => {
            Meta.setData(target, "exp.multipart", "", func);
        }
    }

    static getMultipart(target: { new(): any; }) {
        return Meta.getData(target, "exp.multipart", "") as (mutl: IncomingFormParse) => void;
    }

    static htmlID(id: string | number) {
        return (target: Object, propertyKey: string) => {
            Meta.setData(target.constructor, "exp.htmlid", propertyKey, id);
        }
    }

    static getHtmlID(target: { new(): any; }, field: string) {
        return Meta.getData(target, "exp.htmlid", field) as string;
    }

    /**
     * @装饰int类型数据验证
     * @param func 验证回调函数
     * @param msg 验证失败提示
     * @returns {function(Object, string): undefined}
     */
    static int(func?: (num: IFieldRes<number>) => boolean, msg?: string) {
        return (target: Object, propertyKey: string) => {
            exp.setFieldCheckMetaData<any>(target.constructor, propertyKey, obj => {
                let ret = false;
                if (obj.val == null) {
                    obj.val = obj.defaul;
                }
                else {
                    obj.val = parseInt(obj.val);
                    if (isNaN(obj.val)) {
                        obj.val = obj.defaul;
                        ret = false;
                    }
                    else
                        ret = true;
                }
                if (msg)
                    obj.msg = msg;

                if (func) {
                    ret = func(obj);
                }
                return ret;
            });
        }
    }

    /**
     * 整数+大于0验证
     * @param func
     * @param msg
     * @returns {function(Object, string): undefined}
     */
    static intNotZero(func?: ((num: IFieldRes<number>) => boolean) | null, msg?: string) {
        return (target: Object, propertyKey: string) => {
            exp.setFieldCheckMetaData<any>(target.constructor, propertyKey, obj => {
                let ret = false;
                if (obj.val == null) {
                    obj.val = obj.defaul;
                }
                else {
                    obj.val = parseInt(obj.val);
                    if (isNaN(obj.val)) {
                        obj.val = obj.defaul;
                        ret = false;
                    }
                    else
                        ret = obj.val > 0;
                }
                if (msg)
                    obj.msg = msg;
                else
                    obj.msg = propertyKey + " 必须要大于0"

                if (func) {
                    ret = func(obj);
                }
                return ret;
            });
        }
    }


    /**
     * 可为null整数
     * @param func
     * @param msg
     * @returns {(target:Object, propertyKey:string)=>undefined}
     */
    static intNullAble(func?: ((num: IFieldRes<number | null>) => boolean) | null, msg?: string) {
        return (target: Object, propertyKey: string) => {
            exp.setFieldCheckMetaData<any>(target.constructor, propertyKey, obj => {
                let ret = true;
                if (obj.val == null) {
                    obj.val = null;
                }
                else {
                    obj.val = parseInt(obj.val);
                    if (isNaN(obj.val)) {
                        obj.val = obj.defaul;
                        ret = false;
                    }
                }
                if (msg)
                    obj.msg = msg;

                if (func) {
                    ret = func(obj);
                }
                return ret;
            });
        }
    }

    /**
     * 浮点数验证
     * @param func
     * @param msg
     * @returns {(target:Object, propertyKey:string)=>undefined}
     */
    static float(func?: (num: IFieldRes<number>) => boolean, msg?: string) {
        return (target: Object, propertyKey: string) => {
            exp.setFieldCheckMetaData<any>(target.constructor, propertyKey, obj => {
                let ret = false;
                if (obj.val == null) {
                    obj.val = obj.defaul;
                }
                else {
                    obj.val = parseFloat(obj.val);
                    if (isNaN(obj.val)) {
                        obj.val = obj.defaul;
                        ret = false;
                    }
                    else
                        ret = true;
                }
                if (msg)
                    obj.msg = msg;

                if (func) {
                    ret = func(obj);
                }
                return ret;
            });
        }
    }


    /**
     * 数组验证（默认不遍历数组验证其内容，调用扩展函数：r.val.eachTo）
     * @param func
     * @param msg
     * @returns {(target:Object, propertyKey:string)=>undefined}
     */
    static array<T>(func?: (num: IFieldRes<T[]>) => boolean, msg?: string) {
        return (target: Object, propertyKey: string) => {
            exp.setFieldCheckMetaData<any[]>(target.constructor, propertyKey, obj => {
                let ret = false;
                if (obj.val == null) {
                    obj.val = obj.defaul;
                }
                else if (!Array.isArray(obj.val)) {
                    obj.val = obj.defaul;
                }

                if (msg)
                    obj.msg = msg;

                if (func) {
                    ret = func(obj);
                }
                return ret;
            });
        }
    }

    /**
     * 文件验证
     * @param func
     * @param msg
     * @returns {exp}
     */
    static file(func?: (num: IFieldRes<FileMultiple>) => boolean, msg?: string): FileMultiple {
        return new exp(file => {
            let ret = true;

            if (msg)
                file.msg = msg;

            if (file.val && !file.val.path) {
                return false;
            }

            if (func) {
                ret = func(file);
            }
            return ret;
        }, expType.file) as any;
    }

    /**
     * 数组文件(form表单的name后面加[])
     * @param func
     * @param msg
     * @returns {any}
     */
    static fileArray(func?: (num: IFieldRes<FileMultiple[]>) => boolean, msg?: string): FileMultiple[] {
        return new exp(file => {
            let ret = true;
            if (file.val == null) {
                file.val = [];
            }
            else if (!Array.isArray(file.val)) {
                file.val = [];
            }
            if (msg)
                file.msg = msg;

            if (func) {
                ret = func(file);
            }

            return ret;
        }, expType.file) as any;
    }


    static object<T>(func: (num: IFieldRes<T>) => boolean, msg?: string) {
        return (target: Object, propertyKey: string) => {
            exp.setFieldCheckMetaData<any>(target.constructor, propertyKey, obj => {
                let ret = false;
                if (obj.val == null || typeof obj.val != "object") {
                    obj.val = obj.defaul;
                }
                else {
                    exp.checkObj(obj.val, obj.defaul, obj);

                    if (!obj.ok)
                        return false;
                }

                if (msg)
                    obj.msg = msg;

                if (func) {
                    ret = func(obj);
                }
                return ret;
            });
        }
    }

    /**
     * 字符串+非空验证
     * @param func
     * @param msg
     * @returns {function(Object, string): undefined}
     */
    static stringNotEmpty(func?: RegExp | ((num: IFieldRes<string>) => boolean) | null, msg?: string) {
        return (target: Object, propertyKey: string) => {
            exp.setFieldCheckMetaData<string>(target.constructor, propertyKey, obj => {
                let ret = false;

                if (obj.val == null) {
                    obj.val = obj.defaul;
                }
                else {
                    obj.val = obj.val + "";
                    ret = obj.val.length > 0;
                }
                if (msg)
                    obj.msg = msg;
                else
                    obj.msg = propertyKey + " 不能为空"

                if (func instanceof RegExp) {
                    ret = func.test(obj.val!);
                }
                else if (func instanceof Function) {
                    ret = func(obj);
                }

                return ret;
            });
        }
    }

    static string(func?: RegExp | ((num: IFieldRes<string>) => boolean), msg?: string) {
        return (target: Object, propertyKey: string) => {
            exp.setFieldCheckMetaData<string>(target.constructor, propertyKey, obj => {
                let ret = false;

                if (obj.val == null) {
                    obj.val = obj.defaul;
                }
                else {
                    obj.val = obj.val + "";
                    ret = true;
                }
                if (msg)
                    obj.msg = msg;

                if (func instanceof RegExp) {
                    ret = func.test(obj.val!);
                }
                else if (func instanceof Function) {
                    ret = func(obj);
                }

                return ret;
            });
        }
    }

    static stringNullAble(func?: RegExp | ((num: IFieldRes<string | null>) => boolean), msg?: string) {
        return (target: Object, propertyKey: string) => {
            exp.setFieldCheckMetaData<string | null>(target.constructor, propertyKey, obj => {
                let ret = true;

                if (obj.val == null) {
                    obj.val = null;
                }
                else {
                    obj.val = obj.val + "";
                }
                if (msg)
                    obj.msg = msg;

                if (func instanceof RegExp) {
                    ret = func.test(obj.val!);
                }
                else if (func instanceof Function) {
                    ret = func(obj);
                }

                return ret;
            });
        }
    }

    static checkDocument<T extends T2, T2>(className: { new(): T }, inputObj?: T2): IFieldRes<T> {
        if (inputObj == null) {
            inputObj = {} as any;
        }
        let toObj = new className();
        for (let k in toObj) {
            let id = exp.getHtmlID(className, k);
            if (id != null) {
                let html = df.htmlSelect(id);
                if (html.value !== void 0) {
                    inputObj![k as any] = html.value;
                }
            }
        }

        return exp.checkObj<T>(inputObj!, toObj);

    }

    /**
     *  查找下一个或下下个span
     * @param e
     * @returns {HTMLSpanElement}
     */
    static findNextSpan(e: HTMLElement | null) {
        let span = e ? (e.nextElementSibling || e.nextSibling) as HTMLSpanElement : null;
        if (span && span.localName != "span") {
            span = (span.nextElementSibling || span.nextSibling) as HTMLSpanElement;
            if (span && span.localName != "span") {
                span = null;
            }
        }
        return span
    }

    /**
     * 双向绑定,并进行数据校验,将验证结果传入绑定dom元素的next span
     * @param func 绑定对象
     * @param asyncCheck 异步验证函数
     * @returns {BindFunc}
     */
    static bind(func: (e: HTMLElement | null) => any, asyncCheck?: (a: BindAsync) => void) {
        return df.bind(func, false, (e, val, bindField, bindFunc) => {
            //显示错误信息
            return exp.bindCheck(exp.findNextSpan(e), val, bindField, bindFunc, asyncCheck);
        })
    }

    /**
     * 双向绑定非空验证函数
     * @param func 待绑定对象
     * @param asyncCheck 异步验证函数
     * @returns {BindFunc}
     */
    static bindNotEmpty(func: (e: HTMLElement | null) => any, asyncCheck?: (a: BindAsync) => void) {
        return df.bind(func, false, (e, val, bindField, bindFunc) => {
            let span = exp.findNextSpan(e);

            if (!val || val == "") {
                if (span) {
                    span.innerHTML = "不能为空"
                    if (e && (e as HTMLInputElement).select)
                        (e as HTMLInputElement).select();
                }
                return {
                    err: span ? Error("") : Error("不能为空"),
                    val: val,
                    displayed: !!span,
                }
            }
            if (span)
                span.innerHTML = "";
            return exp.bindCheck(span, val, bindField, bindFunc, asyncCheck);
        })
    }

    /**
     * 进行class字段校验与异步校验
     * @param span
     * @param val
     * @param bindField
     * @param bindFunc
     * @param asyncCheck
     * @returns {any}
     */
    static bindCheck(span: HTMLElement
                         | null, val: any, bindField: BindField, bindFunc: BindFunc, asyncCheck?: (a: BindAsync) => void) {
        let objRes = new IFieldRes<any>();

        function isError(msg: string) {
            if (span)
                span.innerHTML = msg;
            return {
                val: val,
                err: span ? Error("") : sdf.err(msg),
                displayed: span ? true : false,
            }
        }


        if (bindField.fieldName && bindField.parent) {
            //回调函数验证
            var func = exp.getFieldCheckMetaData(bindField.parent.constructor, bindField.fieldName);
            if (func) {
                objRes.val = val;
                objRes.ok = func(objRes);

                if (!objRes.ok) {
                    return isError(objRes.msg);
                }
            }
        }


        if (asyncCheck) {
            let asy = bindFunc.bindAsync;
            if (asy) {//已做过验证
                //值变化,重做验证
                if (val != asy.val) {
                    asy.init(val);
                    return isError(asy.loadMsg);
                }

                if (asy.isLoading) {
                    return isError(asy.loadMsg);
                }

                if (!asy.isOk) {
                    return isError(asy.errMsg);
                }
            }
            else {
                bindFunc.bindAsync = new BindAsync()
                bindFunc.bindAsync.checkFunc = asyncCheck;
                bindFunc.bindAsync.loadCall = msg => {
                    if (span)
                        span.innerHTML = msg;
                }
                if (bindFunc.isEditOnSet) {
                    bindFunc.bindAsync.init(val);
                    return isError(bindFunc.bindAsync.loadMsg);
                }
            }
        }

        if (span)
            span.innerHTML = "";
        return {
            val: val,
            err: null as Error | null,
            displayed: false,
        }
    }

    /**
     * 验证数据
     * @param from
     * @param objRes
     * @returns {IFieldRes<T>}
     */
    static check<T>(from: T, objRes?: IFieldRes<T>): IFieldRes<T> {
        return exp.checkObj(from, new (from.constructor as any), objRes);
    }


    /**
     * 验证并转换数据
     * @param from 待验证数据
     * @param toObj 经类型转换后的结果
     * @param objRes IFieldRes
     * @returns {IFieldRes<T>}
     */
    static checkObj<T>(from: any, toObj: T, objRes?: IFieldRes<T>): IFieldRes<T> {
        if (objRes == null) {
            objRes = new IFieldRes<T>();
        }

        objRes.ok = true;

        for (var key in toObj) {
            objRes.defaul = toObj[key] as any;
            var type = typeof objRes.defaul;

            if (type === "function")
                continue;

            objRes.val = from[key];
            objRes.msg = key + " " + exp.errMsg_;

            if (objRes.defaul instanceof exp) {
                objRes.ok = (objRes.defaul as any as exp).func(objRes);
                toObj[key as any] = objRes.val;
                //验证失败
                if (!objRes.ok) {
                    break;
                }
                continue;
            }

            //回调函数验证
            var func = exp.getFieldCheckMetaData(toObj.constructor, key);
            if (func) {
                objRes.ok = func(objRes);
                toObj[key as any] = objRes.val;

                //验证失败
                if (!objRes.ok) {
                    break;
                }
                continue;
            }


            //无回调,判断default类型
            if (objRes.val == null) {
                objRes.val = objRes.defaul;
            }
            else if (type === "number") {
                objRes.val = parseFloat(objRes.val as any) as any;
                if (isNaN(objRes.val as any))
                    objRes.val = objRes.defaul;
            }
            else if (type === "string") {
                objRes.val = (objRes.val + "") as any;
            }
            else if (objRes.defaul && type === "object") {
                //验证子对象
                if (typeof objRes.val != "object") {
                    objRes.ok = false;
                    break;
                }
                exp.checkObj(objRes.val, objRes.defaul, objRes);
                //验证失败
                if (!objRes.ok) {
                    break;
                }
            }

            toObj[key as any] = objRes.val;
        }

        objRes.val = toObj;
        return objRes;
    }


}

if (typeof window !== "undefined") {
    exp.checkObj = (from, toObj, objRes) => {
        if (!objRes)
            objRes = new IFieldRes<any>();
        objRes.ok = true;
        BindField.initGetBindList(bindList => {
            for (var key in from) {
                bindList.length = 0;
                objRes.defaul = from[key];
                var type = typeof objRes.defaul;

                if (type === "function")
                    continue;


                objRes.val = objRes.defaul;
                objRes.msg = key + " " + exp.errMsg_;

                if (bindList.length > 0) {
                    for (let it of bindList) {
                        for (let bind of it.htmlBind) {
                            if (bind.html && bind.editAble && bind.onSet) {
                                try {
                                    bind.isEditOnSet = false;
                                    let res = bind.onSet(bind.html, objRes.val, it, bind);
                                    if (res.err) {
                                        objRes.msg = res.err.message;
                                        objRes.ok = false;
                                        return;
                                    }
                                } catch (e) {
                                    df.onCatchError(e)
                                }
                            }
                        }
                    }
                }

                if (objRes.defaul && type == "object") {
                    exp.checkObj(objRes.defaul, objRes.defaul, objRes);
                    //验证失败
                    if (!objRes.ok) {
                        break;
                    }
                }
                if (objRes.defaul && objRes.defaul instanceof Array) {
                    for (let arr of objRes.defaul) {
                        if (arr && typeof arr == "object") {
                            exp.checkObj(arr, arr, objRes);
                            //验证失败
                            if (!objRes.ok) {
                                return;
                            }
                        }
                    }
                }

                //回调函数验证
                var func = exp.getFieldCheckMetaData(from.constructor, key);
                if (func) {
                    objRes.ok = func(objRes);
                    //验证失败
                    if (!objRes.ok) {
                        break;
                    }
                    continue;
                }
            }
        });


        return objRes;
    }
}

